/*
 * Created by: Mark Lavrynenko
 * Email:	MarkLavrinenko@gmail.com
 * Project:	  Huffman
*/

#include "HuffmanArchiver.h"
#include "FrequencyTable.h"
#include "CodingTable.h"
#include "BitSequence.h"
#include "HTree.h"
#include "HuffmanOptions.h"
#include "DataSource.h"
#include "DataTarget.h"
#include "IFTBuilder.h"
#include "FrequencyTableCreator.h"
#include "FrequencyTableRestorer.h"
#include "IHTreeBuilder.h"
#include "TreeBuilder.h"
#include <assert.h>

#ifdef _DEBUG
#include <iostream>
#endif

HuffmanArchiver::HuffmanArchiver(): m_decodedSymbols(0), m_table(nullptr),
	m_codingTable(nullptr), m_tree(nullptr)
{
}

HuffmanArchiver::~HuffmanArchiver(void)
{
	if (m_table)
		delete m_table;
	if (m_codingTable)
		delete m_codingTable;
	if (m_tree)
		delete m_tree;
}

void HuffmanArchiver::archive(DataSource* s, DataTarget* t)
{
	m_source = s;
	m_target = t;
	IFTBuilder* ftBuilder = new FrequencyTableCreator();
	m_table = ftBuilder->buildTable(s);
	
	
	writeFrequencyTable(m_table, t);
	s->rewind();

	IHTreeBuilder* treeBuilder = new TreeBuilder();
	m_tree  = treeBuilder->buildTree(m_table);
	m_codingTable =  m_tree->createTable();
	
#ifdef _DEBUG
	size_t sizeInBytes = m_table->totalCount(),
		processed = 0,
		percent = sizeInBytes / 100,
		display = 0;
#endif
	while (s->hasNextByte())
	{
		uint8_t nextByte = s->getNextByte();
		m_codingTable->getCode(nextByte)->serializeInto(m_target);
		// which one way better ??
		//m_target->putBitSequence(m_codingTable->getCode(nextByte));
#ifdef _DEBUG
		++processed;
		if (processed == percent)
		{
			++display;
			std::cout << "One more percent " << display << std::endl;
			processed = 0;
		}
#endif		
	}	
	delete ftBuilder;
	delete treeBuilder;	
}

void HuffmanArchiver::unarchive(DataSource* s, DataTarget* t)
{
	m_source = s;
	m_target = t;
	IFTBuilder* ftBuilder = new FrequencyTableRestorer();
	m_table = ftBuilder->buildTable(s);
	m_estimatedSymbols = m_table->totalCount();	


	IHTreeBuilder* treeBuilder = new TreeBuilder();
	m_tree  = treeBuilder->buildTree(m_table);
	m_tree->myDelegate = this;	
	
	while (s->hasNextByte())
	{
		uint8_t nextByte = s->getNextByte();
		m_tree->unArchivebyte(nextByte);
	}

	delete ftBuilder;
	delete treeBuilder;	
}

void HuffmanArchiver::acceptFinishedByte(uint8_t byte)
{
	if (m_decodedSymbols < m_estimatedSymbols)
		m_target->putNextByte(byte);
	++m_decodedSymbols;
}

void HuffmanArchiver::process(DataSource* s, DataTarget*t, ArchivingMode mode)
{
#ifdef _DEBUG
	std::cout << "Start " << ((ArchivingMode::Archive == mode) ? "archiving." : "unarchiving.") << std::endl;
#endif	
	
#ifdef _DEBUG
	std::cout << "Finished " << ((ArchivingMode::Archive == mode) ? "archiving." : "unarchiving.") << std::endl;
#endif
}

void HuffmanArchiver::writeFrequencyTable(FrequencyTable* table, DataTarget* target)
{
	size_t n;
	uint8_t* repr = table->byteRepresentation(n);
	for (size_t i = 0; i < n; ++i)
	{
		target->putNextByte(repr[i]);
	}
	delete[] repr;
}
